/*
 * Customer.cpp
 *
 *  Created on: Jun 22, 2013
 *      Author: amatzlia
 */

#include "Customer.h"
#include "mtm_ex4.h"
#include <cassert>

Customer::Customer(int id, const string& name) :
		id(id), name(name), moneySpent(0), albumsOwned(0), openOrder(nullptr) {
	if (id <= 0) {
		throw mtm::INVALID_ID();
	}
}

Customer::Customer(int id) :
		id(id), name(""), moneySpent(0), albumsOwned(0), openOrder(nullptr) {
	if (id <= 0) {
		throw mtm::INVALID_ID();
	}
}

Customer::Customer(const Customer& customer) :
		id(customer.id), name(customer.name), moneySpent(customer.moneySpent), albumsOwned(
				customer.albumsOwned), openOrder(nullptr), paidOrders(
				customer.paidOrders) {
	if (customer.openOrder) {
		openOrder = new Order(*(customer.openOrder));
	}
}

Customer::~Customer() {
	if (openOrder) {
		delete openOrder;
	}
}

Customer& Customer::operator=(const Customer& customer) {
	if (&customer == this) {
		return *this;
	}

	id = customer.id;
	name = customer.name;
	moneySpent = customer.moneySpent;
	albumsOwned = customer.albumsOwned;
	if (openOrder) {
		delete openOrder;
		openOrder = nullptr;
	}
	if (customer.openOrder) {
		openOrder = new Order(*(customer.openOrder));
	}
	paidOrders = customer.paidOrders;

	return *this;
}

string Customer::getReportString() const {
	string output = mtm::getCustomerHeaderString(id, name, albumsOwned);

	set<Order, Order::SortById> allOrders(paidOrders);
	if (openOrder) {
		allOrders.insert(*openOrder);
	}
	for (const Order& order : allOrders) {
		output += order.getOrderReport(id);
	}

	return output;
}

string Customer::getDescriptionString() const {
	return mtm::getCustomerDescriptionString(id, name);
}

string Customer::getRevenueString() const {
	return mtm::getRevenueFromCustomerString(id, name, albumsOwned, moneySpent);
}

bool Customer::SortById::operator()(const Customer& customerA,
		const Customer& customerB) const {
	return customerA.id < customerB.id;
}

/* ####################### Private methods ########################### */

void Customer::createNewOrder(int orderId) {
	if (openOrder) {
		throw mtm::CUSTOMER_HAS_OPEN_ORDER();
	}
	openOrder = new Order(orderId);
}

void Customer::addToOpenOrder(int orderId, const Album& album) {
	verifyOpenOrder(orderId);
	openOrder->addAlbum(album);
}

void Customer::removeFromOpenOrder(int orderId, const Album& album) {
	verifyOpenOrder(orderId);
	openOrder->removeAlbum(album);
}

List<Album> Customer::payForOpenOrder(int orderId) {
	verifyOpenOrder(orderId);
	List<Album> albumsToBuy = openOrder->getAlbums();
	if (albumsToBuy.getSize() == 0) {
		throw mtm::ORDER_IS_EMPTY();
	}

	openOrder->pay();
	paidOrders.insert(*openOrder);
	delete openOrder;
	openOrder = nullptr;

	for (Album& album : albumsToBuy) {
		moneySpent += album.purchase();
	}
	albumsOwned += albumsToBuy.getSize();

	return albumsToBuy;
}

void Customer::verifyOpenOrder(int orderId) const {
	if (!openOrder || openOrder->getId() != orderId) {
		assert(orderWasPaid(orderId));
		throw mtm::ORDER_WAS_ALREADY_PAID();
	}
}

bool Customer::orderWasPaid(int orderId) const {
	return paidOrders.find(Order(orderId)) != paidOrders.end();
}
